package com.symaster.common.swing.element.button;

import com.symaster.common.animation.Animation;
import com.symaster.common.animation.Curve;
import com.symaster.common.image.util.ImageUtil;
import com.symaster.common.util.IOUtil;
import org.opencv.core.Mat;
import org.opencv.core.Rect;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;
import java.lang.reflect.InvocationTargetException;

/**
 * @author yinmiao
 * @version 2022-08-26 17:24
 */
public class RotateButton extends AbstractButton {

    private Mat mat;
    private BufferedImage image;
    private int w, h;
    private double marge = 10.0D;
    private double angle = 0.0D;
    private static final Animation.AnimationConfig animationConfig = Animation.AnimationConfig.ofTime(150, 0, 1);
    private static final Animation animation = new Animation();

    public RotateButton() {
        addMouseListener(new MouseListener() {
            @Override
            public void mouseClicked(MouseEvent e) {
                ActionListener[] listeners = listenerList.getListeners(ActionListener.class);
                if (listeners != null) {
                    for (ActionListener listener : listeners) {
                        listener.actionPerformed(null);
                    }
                }
            }

            @Override
            public void mousePressed(MouseEvent e) {

            }

            @Override
            public void mouseReleased(MouseEvent e) {

            }

            @Override
            public void mouseEntered(MouseEvent e) {

            }

            @Override
            public void mouseExited(MouseEvent e) {

            }
        });
    }

    public void setMat(Mat mat) {
        this.mat = mat;
    }

    public double getMarge() {
        return marge;
    }

    public void setMarge(double marge) {
        this.marge = marge;
    }

    public void rotate(double angle) {
        if (angle == this.angle) {
            return;
        }

        double v1 = this.angle;
        double v2 = angle - this.angle;
        this.angle = angle;
        animation.execution(Animation.Sequence.SEQUENCE_SIMULTANEOUSLY, Animation.Strategy.Stop,
                new Animation.AnimationListener(Curve.TYPE_BALANCED, animationConfig) {
            @Override
            public void handler(double factor) {
                double angle = v1 + v2 * factor;

                Mat mat1 = null;
                try {
                    mat1 = ImageUtil.rotateTransform(mat, angle, mat.width() / 2.0D, mat.height() / 2.0D);
                    update(mat1);
                    SwingUtilities.invokeAndWait(RotateButton.this::repaint);
                } catch (InterruptedException | InvocationTargetException e) {
                    throw new RuntimeException(e);
                } finally {
                    IOUtil.close(mat1);
                }
            }
        });
    }

    private void update(Mat mat) {
        if (mat == null) {
            return;
        }
        Mat mat1 = null;
        try {
            mat1 = new Mat(mat, new Rect(0, 0, mat.width(), mat.height()));
            Dimension size = getSize();
            w = size.width;
            h = size.height;
            ImageUtil.scaleMat(mat1, (int) (Math.max(size.width, size.height) - marge));
            image = (BufferedImage) ImageUtil.toBufferedImage(mat1);
        } finally {
            IOUtil.close(mat1);
        }
    }

    @Override
    public void paint(Graphics g) {
        g.setColor(getBackground());
        g.fillRect(0, 0, getWidth(), getHeight());

        if (this.image == null || imageSizeUpdate()) {
            update(mat);
        }

        Dimension size = getSize();

        g.drawImage(image, (size.height - image.getWidth()) / 2, (size.height - image.getHeight()) / 2, null);
    }

    private boolean imageSizeUpdate() {
        Dimension size = getSize();
        if (w != size.getWidth()) {
            return true;
        }
        return h != size.getHeight();
    }
}
